/*
 * Flashlib - QB9 common Flash library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.qb9.flashlib.audio
{
	/**
	 * Manipulates the interaction between IPlayables and AudioMixers.
	 * 
	 * <p><strong>Step 1.</strong> create an AudioManager.</p>
	 * 
	 * <p>Using default PlayableFactory:
	 * <pre>
	 * var manager:AudioManager = new AudioManager();
	 * </pre>
	 * </p>
	 * 
	 * <p>Using a custom PlayableFactory (specifying base url and extension):
	 * <pre>
	 * var factory:PlayableFactory = new PlayableFactory('assets/', 'mp3');
	 * var manager:AudioManager = new AudioManager(factory);
	 * </pre>
	 * </p>
	 * 
	 * <p><strong>Step 2.</strong> Register <code>IPlayable</code>s:</p>
	 * 
	 * <p>Register a Sound object (embedded or manually created):
	 * <pre>
	 * var boing:Sound = new Sound(new URLRequest('assets/boing.mp3'));
	 * manager.registerFx('boing', boing);
	 * </pre>
	 * </p>
	 * 
	 * <p>Register the straight url (will start loading on the first play()):
	 * <pre>
	 * manager.registerFx('boing', 'assets/boing.mp3');
	 * </pre>
	 * </p>
	 * 
	 * <p>If we specified base and extension on the factory and 
	 * the id is the same as the file name, then we can omit it:
	 * <pre>
	 * manager.registerFx('boing');
	 * </pre>
	 * </p>
	 * 
	 * <p>Register a playlist (same options apply to <strong>each</strong> item of the array):
	 * <pre>
	 * manager.registerFx('sequence', ['kick','shout','run']);
	 * </pre>
	 * </p>
	 * 
	 * <p>Register an IPlayable (Audio, Playlist, etc):
	 * <pre>
	 * manager.registerFx('boing', new Audio(boing));
	 * </pre>
	 * </p>
	 * 
	 * <p><strong>Step 3.</strong> Play / stop / pause:</p>
	 * 
	 * <p>Manipulate them by id, on the right AudioMixer:
	 * <pre>
	 * manager.fx.loop('sequence', 5);
	 * manager.music.play('bg_music');
	 * manager.music.pause('another_song');
	 * </pre>
	 * </p>
	 * 
	 * <p>Affect more than one by using many ids:
	 * <pre>
	 * manager.fx.play('kick, shout');
	 * </pre>
	 * </p>
	 * 
	 * <p>Affect EVERY registered asset
	 * <pre>
	 * manager.fx.stop();
	 * manager.music.masterVolume = 0.5;
	 * </pre>
	 * </p>
	 *
	 * <p>The same methods can be found on the manager, they affect all the mixers.
	 * No error will be thrown by the AudioMixers for unknown ids;
	 * so you can just use the manager's methods:
	 * <pre>
	 * manager.resume('another_song, kick, boing');
	 * </pre>
	 * </p>
	 *
	 * <p>Most methods in this class are chainable
	 * (http://en.wikipedia.org/wiki/Fluent_interface).</p>
	 *
	 * @see IPlayable
	 * @see AudioMixer
	 * @see "examples/audio"
	 * @see "examples/audioTask"
	 */

	public class AudioManager implements IAudioManager
	{
		/**
		 * Contains the music Audios
		 */
		public var music:AudioMixer;
		
		/**
		 * Contains the sound effects Audios
		 */
		public var fx:AudioMixer;
		
		protected var factory:PlayableFactory;
		
		/**
		 * Create an AudioManager instance.
		 *
		 * @param factory A PlayableFactory instance that provides methods for creating Iplayable instances. If not set, a default one will be used.
		 */
		public function AudioManager(factory:PlayableFactory = null)
		{
			this.factory = factory || new PlayableFactory();
			
			this.music = new AudioMixer();
			this.fx = new AudioMixer();
		}
		
		/**
		 * Adds a new Audio to the sounds effects AudioMixer.
		 * 
		 * @param id A unique name for this Audio to be used afterwards on the AudioMixer
		 * @param snd A Sound instance.
		 * 
		 * @see #fx
		 * @see flash.media.Sound 
		 */
		
		public function registerFx(id:String, asset : * = null, options:Object = null ) : AudioManager
		{
			register(fx, id, asset, options);
			return this;
		}
		
		/**
		 * Adds a new Audio to the music AudioMixer.
		 * 
		 * @param id A unique name for this Audio to be used afterwards on the AudioMixer
		 * @param snd A Sound instance.
		 * 
		 * @see #music
		 * @see flash.media.Sound 
		 */
		
		public function registerMusic(id:String, asset : * = null, options:Object = null ) : AudioManager
		{
			register(music, id, asset, options);
			return this;
		}
		
		protected function register(mixer:AudioMixer, id:String, asset : *, options:Object) : void
		{
			if (asset == null)
				asset = id; // the id is the URI

			var playable:IPlayable = factory.create(asset, options);
			playable.id = id;
			mixer.add(playable);
		}
		
		protected function delegate(method:String, args:Array) : AudioManager
		{
			fx[method].apply(fx, args);
			music[method].apply(music, args);
			return this;
		}
		
		/** @inheritDoc */
		public function play(id:String = null) : IAudioManager
		{
			return delegate('play', arguments);
		}
		
		/** @inheritDoc */	
		public function loop(id:String = null, times:int = int.MAX_VALUE) : IAudioManager
		{
			return delegate('loop', arguments);
		}
		
		/** @inheritDoc */	
		public function resume(id:String = null) : IAudioManager
		{
			return delegate('resume', arguments);
		}
		
		/** @inheritDoc */	
		public function stop(id:String = null) : IAudioManager
		{
			return delegate('stop', arguments);
		}

		/** @inheritDoc */	
		public function pause(id:String = null) : IAudioManager
		{
			return delegate('pause', arguments);
		}
		
		/** @inheritDoc */	
		public function mute(id:String = null) : IAudioManager
		{
			return delegate('mute', arguments);
		}
		
		/** @inheritDoc */	
		public function unmute(id:String = null) : IAudioManager
		{
			return delegate('unmute', arguments);
		}
		
		/** @inheritDoc */
		public function gain(id:String, gain:Number) : IAudioManager
		{
			return delegate('gain', arguments);
		}
		
		/** @inheritDoc */
		public function remove(id:String = null) : IAudioManager
		{
			return delegate('remove', arguments);
		}
		
		/** @inheritDoc */
		public function has(id:String) : Boolean
		{
			return music.has(id) || fx.has(id);
		}
		
		/** @inheritDoc */
		public function retrieve(id:String) : IPlayable
		{
			return music.retrieve(id) || fx.retrieve(id);
		}
		
		/** @inheritDoc */
		public function get masterVolume() : Number
		{
			// Only retrieve it if it's normalized to one single value
			return fx.masterVolume == music.masterVolume ? fx.masterVolume : NaN;
		}
		
		/** @inheritDoc */
		public function set masterVolume(num:Number) : void
		{
			fx.masterVolume = music.masterVolume = num;
		}
	}
}
